package zorm

import (
	"database/sql"
	"errors"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"

	"gitee.com/chunanyong/zorm/gouuid"
)

//wrapPageSQL：包装分页的SQL语句
//wrapPageSQL：SQL statement for wrapping paging.
func wrapPageSQL(dbType string, sqlstr string, page *Page) (string, error) {
	//查询order by 的位置.为了保持各个数据库之间的分页语句兼容,要求都要有order by,不然迁移数据库时的风险就很大了
	//新的分页方法都已经不需要order by了,不再强制检查
	//The new paging method does not require 'order by' anymore, no longer mandatory check.
	//
	/*
		locOrderBy := findOrderByIndex(sqlstr)
		if len(locOrderBy) <= 0 { //如果没有 order by
			return "", errors.New("分页语句必须有 order by")
		}
	*/
	var sqlbuilder strings.Builder
	sqlbuilder.WriteString(sqlstr)
	if dbType == "mysql" || dbType == "sqlite" || dbType == "dm" || dbType == "gbase" { //MySQL,sqlite3,dm数据库,南大通用
		sqlbuilder.WriteString(" LIMIT ")
		sqlbuilder.WriteString(strconv.Itoa(page.PageSize * (page.PageNo - 1)))
		sqlbuilder.WriteString(",")
		sqlbuilder.WriteString(strconv.Itoa(page.PageSize))

	} else if dbType == "postgresql" || dbType == "kingbase" || dbType == "shentong" { //postgresql,kingbase,神通数据库
		sqlbuilder.WriteString(" LIMIT ")
		sqlbuilder.WriteString(strconv.Itoa(page.PageSize))
		sqlbuilder.WriteString(" OFFSET ")
		sqlbuilder.WriteString(strconv.Itoa(page.PageSize * (page.PageNo - 1)))
	} else if dbType == "mssql" || dbType == "oracle" { //sqlserver 2012+,oracle 12c+
		sqlbuilder.WriteString(" OFFSET ")
		sqlbuilder.WriteString(strconv.Itoa(page.PageSize * (page.PageNo - 1)))
		sqlbuilder.WriteString(" ROWS FETCH NEXT ")
		sqlbuilder.WriteString(strconv.Itoa(page.PageSize))
		sqlbuilder.WriteString(" ROWS ONLY ")
	} else if dbType == "db2" { //db2

		//先不写啦
		//bug(springrain) 还需要其他的数据库分页语句
	}
	sqlstr = sqlbuilder.String()
	return reBindSQL(dbType, sqlstr)
}

//wrapInsertSQL: 包装保存Struct语句.返回语句,是否自增,错误信息
//数组传递,如果外部方法有调用append的逻辑，append会破坏指针引用，所以传递指针.

//wrapInsertSQL:Pack and save 'Struct' statement. Return  SQL statement, whether it is incremented, error message.
//Array transfer, if the external method has logic to call append, append will destroy the pointer reference, so the pointer is passed.
func wrapInsertSQL(dbType string, typeOf reflect.Type, entity IEntityStruct, columns *[]reflect.StructField, values *[]interface{}) (string, int, string, error) {
	sqlstr, autoIncrement, pktype, err := wrapInsertSQLNOreBuild(dbType, typeOf, entity, columns, values)
	savesql, err := reBindSQL(dbType, sqlstr)
	return savesql, autoIncrement, pktype, err
}

//wrapInsertSQLNOreBuild 包装保存Struct语句.返回语句,没有rebuild,返回原始的SQL,是否自增,错误信息
//数组传递,如果外部方法有调用append的逻辑,传递指针,因为append会破坏指针引用

//Pack and save Struct statement. Return  SQL statement, no rebuild, return original SQL, whether it is self-increment, error message.
//Array transfer, if the external method has logic to call append, append will destroy the pointer reference, so the pointer is passed.
func wrapInsertSQLNOreBuild(dbType string, typeOf reflect.Type, entity IEntityStruct, columns *[]reflect.StructField, values *[]interface{}) (string, int, string, error) {

	//自增类型  0(不自增),1(普通自增),2(序列自增),3(触发器自增)
	//Self-increment type： 0（Not increase）,1(Ordinary increment),2(Sequence increment),3(Trigger increment)
	autoIncrement := 0
	//主键类型
	//Primary key type
	pktype := ""
	//SQL语句的构造器
	//SQL statement constructor
	var sqlBuilder strings.Builder
	sqlBuilder.WriteString("INSERT INTO ")
	sqlBuilder.WriteString(entity.GetTableName())
	sqlBuilder.WriteString("(")

	//SQL语句中,VALUES(?,?,...)语句的构造器
	//In the SQL statement, the constructor of the VALUES(?,?,...) statement
	var valueSQLBuilder strings.Builder
	valueSQLBuilder.WriteString(" VALUES (")
	//主键的名称
	//The name of the primary key.
	pkFieldName, e := entityPKFieldName(entity, typeOf)
	if e != nil {
		return "", autoIncrement, pktype, e
	}

	var sequence string
	var sequenceOK bool
	if entity.GetPkSequence() != nil {
		sequence, sequenceOK = entity.GetPkSequence()[dbType]
		if sequenceOK { //存在序列 Existence sequence
			if sequence == "" { //触发器自增,也兼容自增关键字 Auto-increment by trigger, also compatible with auto-increment keywords.
				autoIncrement = 3
			} else { //序列自增 Sequence increment
				autoIncrement = 2
			}

		}

	}

	for i := 0; i < len(*columns); i++ {
		field := (*columns)[i]

		if field.Name == pkFieldName { //如果是主键 If it is the primary key
			//获取主键类型 Get the primary key type.
			pkKind := field.Type.Kind()
			if pkKind == reflect.String {
				pktype = "string"
			} else if pkKind == reflect.Int || pkKind == reflect.Int32 || pkKind == reflect.Int16 || pkKind == reflect.Int8 {
				pktype = "int"
			} else if pkKind == reflect.Int64 {
				pktype = "int64"
			} else {
				return "", autoIncrement, pktype, errors.New("wrapInsertSQLNOreBuild不支持的主键类型")
			}

			if autoIncrement == 3 {
				//如果是后台触发器生成的主键值,sql语句中不再体现
				// If it is the primary key value generated by the background trigger, it is no longer reflected in the sql statement.
				//去掉这一列,后续不再处理
				//Remove this column and will not process it later.
				*columns = append((*columns)[:i], (*columns)[i+1:]...)
				*values = append((*values)[:i], (*values)[i+1:]...)
				i = i - 1
				continue
			}

			//主键的值
			//The value of the primary key.
			pkValue := (*values)[i]
			if autoIncrement == 2 { //如果是序列自增 | If it is a sequence increment
				//拼接字符串 | Concatenated string
				//sqlBuilder.WriteString(getStructFieldTagColumnValue(typeOf, field.Name))
				sqlBuilder.WriteString(field.Tag.Get(tagColumnName))
				sqlBuilder.WriteString(",")
				valueSQLBuilder.WriteString(sequence)
				valueSQLBuilder.WriteString(",")
				//去掉这一列,后续不再处理
				//Remove this column and will not process it later.
				*columns = append((*columns)[:i], (*columns)[i+1:]...)
				*values = append((*values)[:i], (*values)[i+1:]...)
				i = i - 1
				continue

			} else if (pktype == "string") && (pkValue.(string) == "") { //主键是字符串类型,并且值为"",赋值id
				//生成主键字符串
				//Generate primary key string
				id := FuncGenerateStringID()
				(*values)[i] = id
				//给对象主键赋值
				//Assign a value to the primary key of the object
				v := reflect.ValueOf(entity).Elem()
				v.FieldByName(field.Name).Set(reflect.ValueOf(id))
				//如果是数字类型,并且值为0,认为是数据库自增,从数组中删除掉主键的信息,让数据库自己生成
				//If it is a number type and the value is 0,
				//it is considered to be a database self-increment,
				//delete the primary key information from the array, and let the database generate itself.
			} else if (pktype == "int" && pkValue.(int) == 0) || (pktype == "int64" && pkValue.(int64) == 0) {
				//标记是自增主键
				//Mark is auto-incrementing primary key
				autoIncrement = 1
				//去掉这一列,后续不再处理
				//Remove this column and will not process it later.
				*columns = append((*columns)[:i], (*columns)[i+1:]...)
				*values = append((*values)[:i], (*values)[i+1:]...)
				i = i - 1
				continue
			}
		}
		//拼接字符串
		//Concatenated string.
		//sqlBuilder.WriteString(getStructFieldTagColumnValue(typeOf, field.Name))
		sqlBuilder.WriteString(field.Tag.Get(tagColumnName))
		sqlBuilder.WriteString(",")
		valueSQLBuilder.WriteString("?,")

	}
	//去掉字符串最后的 ','
	//Remove the',' at the end of the string
	sqlstr := sqlBuilder.String()
	if len(sqlstr) > 0 {
		sqlstr = sqlstr[:len(sqlstr)-1]
	}
	valuestr := valueSQLBuilder.String()
	if len(valuestr) > 0 {
		valuestr = valuestr[:len(valuestr)-1]
	}
	sqlstr = sqlstr + ")" + valuestr + ")"
	//savesql, err := wrapSQL(dbType, sqlstr)
	return sqlstr, autoIncrement, pktype, nil

}

//wrapInsertSliceSQL 包装批量保存StructSlice语句.返回语句,是否自增,错误信息
//数组传递,如果外部方法有调用append的逻辑，append会破坏指针引用，所以传递指针
//wrapInsertSliceSQL：Package and save Struct Slice statements in batches. Return SQL statement, whether it is incremented, error message.
//Array transfer, if the external method has logic to call append, append will destroy the pointer reference, so the pointer is passed
func wrapInsertSliceSQL(dbType string, typeOf reflect.Type, entityStructSlice []IEntityStruct, columns *[]reflect.StructField, values *[]interface{}) (string, int, error) {
	sliceLen := len(entityStructSlice)
	if entityStructSlice == nil || sliceLen < 1 {
		return "", 0, errors.New("wrapInsertSliceSQL对象数组不能为空")
	}

	//第一个对象,获取第一个Struct对象,用于获取数据库字段,也获取了值
	//The first object, get the first Struct object, used to get the database field, and also get the value
	entity := entityStructSlice[0]

	//先生成一条语句
	//Generate a statement first.
	sqlstr, autoIncrement, _, firstErr := wrapInsertSQLNOreBuild(dbType, typeOf, entity, columns, values)
	if firstErr != nil {
		return "", autoIncrement, firstErr
	}
	//如果只有一个Struct对象
	//If there is only one Struct object.
	if sliceLen == 1 {
		sqlstr, _ = reBindSQL(dbType, sqlstr)
		return sqlstr, autoIncrement, firstErr
	}
	//主键的名称
	//The name of the primary key.
	pkFieldName, e := entityPKFieldName(entity, typeOf)
	if e != nil {
		return "", autoIncrement, e
	}

	//截取生成的SQL语句中 VALUES 后面的字符串值
	//Intercept the string value after VALUES in the generated SQL statement.
	valueIndex := strings.Index(sqlstr, " VALUES (")
	if valueIndex < 1 { //生成的语句异常
		return "", autoIncrement, errors.New("wrapInsertSliceSQL生成的语句异常")
	}
	//value后面的字符串 例如 (?,?,?),用于循环拼接
	//The string after the value, such as (?,?,?), is used for circular splicing
	valuestr := sqlstr[valueIndex+8:]
	//SQL语句的构造器
	//SQL statement constructor
	var insertSliceSQLBuilder strings.Builder
	insertSliceSQLBuilder.WriteString(sqlstr)
	for i := 1; i < sliceLen; i++ {
		//拼接字符串
		//Splicing string
		insertSliceSQLBuilder.WriteString(",")
		insertSliceSQLBuilder.WriteString(valuestr)

		entityStruct := entityStructSlice[i]
		for j := 0; j < len(*columns); j++ {
			// 获取实体类的反射,指针下的struct
			// Get the reflection of the entity class, the struct under the pointer.
			valueOf := reflect.ValueOf(entityStruct).Elem()
			field := (*columns)[j]
			if field.Name == pkFieldName { //如果是主键 ｜ If it is the primary key
				pkKind := field.Type.Kind()
				//主键的值
				//The value of the primary key
				pkValue := valueOf.FieldByName(field.Name).Interface()
				//只处理字符串类型的主键,其他类型,columns中并不包含
				//Only handle primary keys of string type, other types, not included in columns.
				if (pkKind == reflect.String) && (pkValue.(string) == "") {
					//主键是字符串类型,并且值为"",赋值'id'
					//The primary key is a string type, and the value is "", assigned the value'id' .
					//生成主键字符串
					//Generate primary key string
					id := FuncGenerateStringID()
					*values = append(*values, id)
					//给对象主键赋值
					//Assign a value to the primary key of the object
					valueOf.FieldByName(field.Name).Set(reflect.ValueOf(id))
					continue
				}
			}

			//给字段赋值
			//Assign a value to the field.
			*values = append(*values, valueOf.FieldByName(field.Name).Interface())

		}
	}

	//包装sql
	//Wrap sql
	savesql, err := reBindSQL(dbType, insertSliceSQLBuilder.String())
	return savesql, autoIncrement, err

}

//wrapUpdateSQL: 包装更新Struct语句
//数组传递,如果外部方法有调用append的逻辑，append会破坏指针引用，所以传递指针.
//wrapUpdateSQL: Package update Struct statement.
//Array transfer, if the external method has logic to call append, append will destroy the pointer reference, so the pointer is passed.
func wrapUpdateSQL(dbType string, typeOf reflect.Type, entity IEntityStruct, columns *[]reflect.StructField, values *[]interface{}, onlyUpdateNotZero bool) (string, error) {

	//SQL语句的构造器
	//SQL statement constructor
	var sqlBuilder strings.Builder
	sqlBuilder.WriteString("UPDATE ")
	sqlBuilder.WriteString(entity.GetTableName())
	sqlBuilder.WriteString(" SET ")

	//主键的值
	//The value of the primary key
	var pkValue interface{}
	//主键的名称
	//The name of the primary key
	pkFieldName, e := entityPKFieldName(entity, typeOf)
	if e != nil {
		return "", e
	}

	for i := 0; i < len(*columns); i++ {
		field := (*columns)[i]
		if field.Name == pkFieldName {
			//如果是主键
			//If it is the primary key.
			pkValue = (*values)[i]
			//去掉这一列,最后处理主键
			//Remove this column, and finally process the primary key.
			*columns = append((*columns)[:i], (*columns)[i+1:]...)
			*values = append((*values)[:i], (*values)[i+1:]...)
			i = i - 1
			continue
		}

		//如果是默认值字段,删除掉,不更新
		//If it is the default value field, delete it and do not update.
		if onlyUpdateNotZero && (reflect.ValueOf((*values)[i]).IsZero()) {
			//去掉这一列,不再处理
			//Remove this column and no longer process
			*columns = append((*columns)[:i], (*columns)[i+1:]...)
			*values = append((*values)[:i], (*values)[i+1:]...)
			i = i - 1
			continue

		}
		//sqlBuilder.WriteString(getStructFieldTagColumnValue(typeOf, field.Name))
		sqlBuilder.WriteString(field.Tag.Get(tagColumnName))
		sqlBuilder.WriteString("=?,")

	}
	//主键的值是最后一个
	//The value of the primary key is the last.
	*values = append(*values, pkValue)
	//去掉字符串最后的 ','
	//Remove the',' at the end of the string.
	sqlstr := sqlBuilder.String()
	sqlstr = sqlstr[:len(sqlstr)-1]

	sqlstr = sqlstr + " WHERE " + entity.GetPKColumnName() + "=?"

	return reBindSQL(dbType, sqlstr)
}

//wrapDeleteSQL 包装删除Struct语句
//wrapDeleteSQL:Package delete Struct statement.
func wrapDeleteSQL(dbType string, entity IEntityStruct) (string, error) {

	//SQL语句的构造器
	//SQL statement constructor
	var sqlBuilder strings.Builder
	sqlBuilder.WriteString("DELETE FROM ")
	sqlBuilder.WriteString(entity.GetTableName())
	sqlBuilder.WriteString(" WHERE ")
	sqlBuilder.WriteString(entity.GetPKColumnName())
	sqlBuilder.WriteString("=?")
	sqlstr := sqlBuilder.String()

	return reBindSQL(dbType, sqlstr)

}

//wrapInsertEntityMapSQL: 包装保存Map语句,Map因为没有字段属性,无法完成Id的类型判断和赋值,需要确保Map的值是完整的.
//wrapInsertEntityMapSQL:Pack and save the Map statement. Because Map does not have field attributes,
//it cannot complete the type judgment and assignment of Id. It is necessary to ensure that the value of Map is complete.
func wrapInsertEntityMapSQL(dbType string, entity IEntityMap) (string, []interface{}, bool, error) {
	//是否自增,默认false
	autoIncrement := false
	dbFieldMap := entity.GetDBFieldMap()
	if len(dbFieldMap) < 1 {
		return "", nil, autoIncrement, errors.New("wrapInsertEntityMapSQL-->GetDBFieldMap返回值不能为空")
	}
	//SQL对应的参数
	//SQL corresponding parameters
	values := []interface{}{}

	//SQL语句的构造器
	//SQL statement constructor
	var sqlBuilder strings.Builder
	sqlBuilder.WriteString("INSERT INTO ")
	sqlBuilder.WriteString(entity.GetTableName())
	sqlBuilder.WriteString("(")

	//SQL语句中,VALUES(?,?,...)语句的构造器
	//In the SQL statement, the constructor of the VALUES(?,?,...) statement.
	var valueSQLBuilder strings.Builder
	valueSQLBuilder.WriteString(" VALUES (")
	//是否Set了主键
	//Whether the primary key is set.
	_, hasPK := dbFieldMap[entity.GetPKColumnName()]
	if !hasPK { //如果没有设置主键,认为是自增或者序列 | If the primary key is not set, it is considered to be auto-increment or sequence
		autoIncrement = true
		if sequence, ok := entity.GetPkSequence()[dbType]; ok { //如果是序列 | If it is a sequence.
			sqlBuilder.WriteString(entity.GetPKColumnName())
			sqlBuilder.WriteString(",")
			valueSQLBuilder.WriteString(sequence)
			valueSQLBuilder.WriteString(",")
		}
	}

	for k, v := range dbFieldMap {
		//拼接字符串
		//Concatenated string
		sqlBuilder.WriteString(k)
		sqlBuilder.WriteString(",")
		valueSQLBuilder.WriteString("?,")
		values = append(values, v)
	}
	//去掉字符串最后的 ','
	//Remove the',' at the end of the string
	sqlstr := sqlBuilder.String()
	if len(sqlstr) > 0 {
		sqlstr = sqlstr[:len(sqlstr)-1]
	}
	valuestr := valueSQLBuilder.String()
	if len(valuestr) > 0 {
		valuestr = valuestr[:len(valuestr)-1]
	}
	sqlstr = sqlstr + ")" + valuestr + ")"

	var e error
	sqlstr, e = reBindSQL(dbType, sqlstr)
	if e != nil {
		return "", nil, autoIncrement, e
	}
	return sqlstr, values, autoIncrement, nil
}

//wrapUpdateEntityMapSQL: 包装Map更新语句,Map因为没有字段属性,无法完成Id的类型判断和赋值,需要确保Map的值是完整的.
//wrapUpdateEntityMapSQL: Wrap the Map update statement. Because Map does not have field attributes,
//it cannot complete the type judgment and assignment of Id. It is necessary to ensure that the value of Map is complete.
func wrapUpdateEntityMapSQL(dbType string, entity IEntityMap) (string, []interface{}, error) {
	dbFieldMap := entity.GetDBFieldMap()
	if len(dbFieldMap) < 1 {
		return "", nil, errors.New("wrapUpdateEntityMapSQL-->GetDBFieldMap返回值不能为空")
	}
	//SQL语句的构造器
	//SQL statement constructor
	var sqlBuilder strings.Builder
	sqlBuilder.WriteString("UPDATE ")
	sqlBuilder.WriteString(entity.GetTableName())
	sqlBuilder.WriteString(" SET ")

	//SQL对应的参数
	//SQL corresponding parameters.
	values := []interface{}{}
	//主键名称
	//Primary key name
	var pkValue interface{}

	for k, v := range dbFieldMap {

		if k == entity.GetPKColumnName() { //如果是主键  | If it is the primary key
			pkValue = v
			continue
		}

		//拼接字符串 | Splicing string.
		sqlBuilder.WriteString(k)
		sqlBuilder.WriteString("=?,")
		values = append(values, v)
	}
	//主键的值是最后一个
	//The value of the primary key is the last
	values = append(values, pkValue)
	//去掉字符串最后的 ','
	//Remove the',' at the end of the string
	sqlstr := sqlBuilder.String()
	sqlstr = sqlstr[:len(sqlstr)-1]

	sqlstr = sqlstr + " WHERE " + entity.GetPKColumnName() + "=?"

	var e error
	sqlstr, e = reBindSQL(dbType, sqlstr)
	if e != nil {
		return "", nil, e
	}
	return sqlstr, values, nil
}

//wrapQuerySQL: 封装查询语句
//wrapQuerySQL: Encapsulated query statement
func wrapQuerySQL(dbType string, finder *Finder, page *Page) (string, error) {

	//获取到没有page的sql的语句
	//Get the SQL statement without page.
	sqlstr, err := finder.GetSQL()
	if err != nil {
		return "", err
	}
	if page == nil {
		sqlstr, err = reBindSQL(dbType, sqlstr)
	} else {
		sqlstr, err = wrapPageSQL(dbType, sqlstr, page)
	}

	if err != nil {
		return "", err
	}
	return sqlstr, err
}

//reBindSQL: 包装基础的SQL语句,根据数据库类型,调整SQL变量符号,例如?,? $1,$2这样的
//reBindSQL: Pack basic SQL statements, adjust the SQL variable symbols according to the database type, such as?,? $1,$2
func reBindSQL(dbType string, sqlstr string) (string, error) {
	if dbType == "mysql" || dbType == "sqlite" || dbType == "dm" || dbType == "gbase" {
		return sqlstr, nil
	}

	strs := strings.Split(sqlstr, "?")
	if len(strs) < 1 {
		return sqlstr, nil
	}
	var sqlBuilder strings.Builder
	sqlBuilder.WriteString(strs[0])
	for i := 1; i < len(strs); i++ {
		if dbType == "postgresql" || dbType == "kingbase" { //postgresql,kingbase
			sqlBuilder.WriteString("$")
			sqlBuilder.WriteString(strconv.Itoa(i))
		} else if dbType == "mssql" { //mssql
			sqlBuilder.WriteString("@p")
			sqlBuilder.WriteString(strconv.Itoa(i))
		} else if dbType == "oracle" || dbType == "shentong" { //oracle,神州通用
			sqlBuilder.WriteString(":")
			sqlBuilder.WriteString(strconv.Itoa(i))
		} else { //其他情况,还是使用 '?' | In other cases, or use'?'
			sqlBuilder.WriteString("?")
		}
		sqlBuilder.WriteString(strs[i])
	}
	return sqlBuilder.String(), nil
}

//查询'order by'在sql中出现的开始位置和结束位置
//Query the start position and end position of'order by' in SQL.
var orderByExpr = "\\s+(order)\\s+(by)+\\s"
var orderByRegexp, _ = regexp.Compile(orderByExpr)

//findOrderByIndex 查询order by在sql中出现的开始位置和结束位置
// findOrderByIndex: Query the start position and end position of'order by' in SQL.
func findOrderByIndex(strsql string) []int {
	loc := orderByRegexp.FindStringIndex(strings.ToLower(strsql))
	return loc
}

//查询'group by'在sql中出现的开始位置和结束位置
//Query the start position and end position of'group by' in sql。
var groupByExpr = "\\s+(group)\\s+(by)+\\s"
var groupByRegexp, _ = regexp.Compile(groupByExpr)

//findGroupByIndex: 查询group by在sql中出现的开始位置和结束位置
//findGroupByIndex:Query the start position and end position of'group by' in sql。
func findGroupByIndex(strsql string) []int {
	loc := groupByRegexp.FindStringIndex(strings.ToLower(strsql))
	return loc
}

//查询 from 在sql中出现的开始位置和结束位置
//Query the start position and end position of 'from' in sql
var fromExpr = "\\s+(from)+\\s"
var fromRegexp, _ = regexp.Compile(fromExpr)

//findFromIndexa: 查询from在sql中出现的开始位置和结束位置
//findFromIndex:Query the start position and end position of 'from' in sql
func findFromIndex(strsql string) []int {
	loc := fromRegexp.FindStringIndex(strings.ToLower(strsql))
	return loc
}

//converValueColumnType 根据数据库的字段类型,转化成golang的类型,不处理sql.Nullxxx类型
//converValueColumnType: According to the field type of the database, it is converted to the type of golang, and the sql.Nullxxx type is not processed
func converValueColumnType(v interface{}, columnType *sql.ColumnType) interface{} {

	if v == nil {
		return nil
	}

	//如果是字节数组
	//If it is a byte array
	value, ok := v.([]byte)
	if !ok { //转化失败,不是字节数组,例如:string,直接返回值
		return v
	}
	if len(value) < 1 { //值为空,为nil
		return value
	}

	//获取数据库类型,自己对应golang的基础类型值,不处理sql.Nullxxx类型
	//Get the database type, corresponding to the basic type value of golang, and do not process the sql.Nullxxx type.
	databaseTypeName := strings.ToUpper(columnType.DatabaseTypeName())
	switch databaseTypeName {
	case "CHAR", "NCHAR", "VARCHAR", "NVARCHAR", "VARCHAR2", "NVARCHAR2", "TINYTEXT", "MEDIUMTEXT", "TEXT", "NTEXT", "LONGTEXT", "LONG":
		return typeConvertString(v)
	case "INT", "INT4", "INTEGER", "SERIAL", "TINYINT", "BIT", "SMALLINT", "SMALLSERIAL", "INT2":
		return typeConvertInt(v)
	case "BIGINT", "BIGSERIAL", "INT8":
		return typeConvertInt64(v)
	case "FLOAT", "REAL":
		return typeConvertFloat32(v)
	case "DOUBLE":
		return typeConvertFloat64(v)
	case "DECIMAL", "NUMBER", "NUMERIC", "DEC":
		return typeConvertDecimal(v)
	case "DATE":
		return typeConvertTime(v, "2006-01-02", time.Local)
	case "TIME":
		return typeConvertTime(v, "15:04:05", time.Local)
	case "DATETIME":
		return typeConvertTime(v, "2006-01-02 15:04:05", time.Local)
	case "TIMESTAMP":
		return typeConvertTime(v, "2006-01-02 15:04:05.000", time.Local)
	case "BOOLEAN", "BOOL":
		return typeConvertBool(v)
	}
	//其他类型以后再写.....
	//Other types will be written later...
	return v
}

//FuncGenerateStringID 默认生成字符串ID的函数.方便自定义扩展
//FuncGenerateStringID:Function to generate string ID by default. Convenient for custom extension
var FuncGenerateStringID func() string = generateStringID

//generateStringID: 生成主键字符串
//generateStringID: Generate primary key string.
func generateStringID() string {
	//pk := strconv.FormatInt(time.Now().UnixNano(), 10)
	pk, errUUID := gouuid.NewV4()
	if errUUID != nil {
		return ""
	}
	return pk.String()
}
